/**
 * @file usb_capture_with_thread.h
 * @author XiaMo WangMH
 * @brief
 * 针对VideoCapture读取速度慢和分辨率不足的问题进行了重写，主要接口保持一致
 * @version 1.1
 * @date 2021-08-27
 *
 * @copyright Copyright (c) 2021
 *
 */

 #pragma once

 #ifndef USBCAPTURE_WITH_THREAD_H
 #define USBCAPTURE_WITH_THREAD_H
 
 #include <iostream>
 #include <mutex>
 #include <thread>
 #include <atomic>
 
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include "linux/videodev2.h"
 
 #include </opt/libjpeg-turbo/include/turbojpeg.h>
 #include <opencv2/imgcodecs.hpp>
 #include <opencv2/opencv.hpp>
 
 #define CLEAR(x) memset(&(x), 0, sizeof(x))  // 针对任意类型变量的底层数据清除
 
 namespace wmj {
 enum OPERATION_ID {
     ALL_AUTO = 0x00,
     READ_YAML,
     EXPOSURE_TIME,
     BRIGTHNESS,
     GAMMA,
     CONTRAST,
     SATURATION,
     SHARPNESS,
     GAIN,
     WHITE_BALANCE_B,
     WHITE_BALANCE_R,
     WHITE_BALANCE_TEMPERATURE,
     WHITE_BALANCE_AUTO
 };
 struct CameraParam {
     int SetParam;
     int VideoRate;
     int Height;
     int Width;
     int ExposureTime;
     int Brightness;
     int Contrast;
     int Gamma;
     int Satuation;
     struct WhiteBalance {
         int G;
         int R;
         int B;
     } WhiteBalance;
     int Gain;
     cv::Mat CameraMatrix;  // 内参
     cv::Mat DistCoeffs;    // 畸变
 };
 
 class UVCCamera {
    public:
     /**
      * @brief 构造函数，开启线程
      *
      * @param cameraPath 摄像头设备路径 eg: /dev/video0
      */
     UVCCamera(std::string cameraPath);
 
    private:
     u_int32_t n_buffers;  // 缓冲区队列长度
     struct videoBuffer {
         uint8_t* pBuffer;
         uint32_t len;
         int width;
         int height;
     }* initBuffer;  // 缓冲区
     /**
      *  通过Yaml文件第一行查找所有相机，并依次打开，如果打开成功则设置填入参数到map
      */
     int loadSerialOpenDevice(std::string cameraPath);
     bool m_debug;  // 是否debug
     /**
      * @brief 尝试打开设备接口
      *
      * @return int 返回设备描述符,0为可以打开
      */
     int tryOpenDevice(std::string source, std::string orientation);
     void readCameraParam(std::string orientation, CameraParam& cameraParamTemp);
     int setCameraParam(std::string orientation, CameraParam cameraParamTemp);
     int init3rdParty();
     int free3rdParty();
     // 可见设备数。在打开设备时数据更新。
     uint32_t m_device_num = 0;
     // orientation对应的GxI设备句柄。在打开设备时，以下这些map中的相机orientation对应的key被初始化
     std::map<std::string, std::string> m_source;
     std::map<std::string, std::mutex> m_mutex;
     std::map<std::string, CameraParam> m_param;
     std::map<std::string, int> m_handles;
     std::map<std::string, videoBuffer*> m_buffers;
     std::map<std::string, cv::Mat> m_imgbuffers;  // 要输出的图像
     std::map<std::string, bool> m_ifupdate;
     tjhandle _JPEG_Decoder;  // turbo库解码器柄
     /**
      * @brief 返回相机内参矩阵
      *
      * @return cv::Mat
      */
     cv::Mat getCameraMatrix(std::string orientation);
     /**
      * @brief 返回相机畸变矩阵
      *
      * @return cv::Mat
      */
     cv::Mat getDistCoeffs(std::string orientation);
     // 用于存放RGB图像数据的缓冲信息
    private:
     // 标志位,带_的都是整个类的标志位,以后可能会用到
     // uint8_t m_captureState;  // 状态量
     // 0是捕捉，1是暂停，2退出主循环，终止线程。 bool m_isOpened;         //
     // 摄像头是否打开 bool m_isUpdated;        // 图像是否更新 bool
     // m_isOperatingBuf;   // 是否在操作缓冲区 bool m_recordVideo;      //
     // 是否录视频 bool m_setParam;         // 是否设置参数 bool m_undistort; //
     // 是否矫正畸变 bool m_captureStop;      // 标记摄像头进程是否已经终止 bool
     // m_recordVideoStop;  // 标记录视频进程是否已经终止
     std::thread _captureThread;  // 摄像头线程
     /**
      * @brief 重新读取yaml中的相机参数矩阵
      *
      * @param cameraName yaml文件中的第一级结构名
      */
     void reReadCameraMatrix(std::string cameraName = "camera0");
     /**
      * @brief 初始化设备,设置像素格式,图像大小，扫描方式，初始化缓冲区
      *
      * @return int
      */
     int initDevice();
 
     /**
      * @brief 关闭设备接口
      *
      * @return int
      */
     int closeDevice();
     /**
      * @brief 初始化缓冲区，建立内存映射
      *
      * @return int -1 申请缓冲区失败 -2 映射内存失败 正常则返回缓冲区队列大小
      */
     int initBuffers();
     /**
      * @brief 销毁缓冲区
      *
      * @return int -1 取消内存映射失败 正常返回0
      */
     int freeBuffers();
     /**
      * @brief 写入缓冲区，打开流
      *
      * @return int -1 入队列失败 正常则返回0
      */
     int startStream();
     /**
      * @brief 关闭流
      *
      * @return int -1 关闭失败 正常返回0
      */
     int stopStream();
     /**
      * @brief 读取视频的线程函数
      *
      */
     void mainRun();
     /**
      * @brief 读取一帧视频图像
      *
      * @return int -1 出队列操作错误； -2 入队列操作错误； -3 读取队列失败； -4
      * 解码失败； 正常返回0
      */
     int getFrameRaw();
     /**
      * @brief
      * 检查得到的图像大小，如果得到的图像大小和目标不匹配，就将其更新为当前图像大小
      *
      */
     int ResetImageSize();
     /**
      * @brief 设置摄像头参数
      *
      * @return int
      */
     int setParam();
     /*设置曝光时间*/
     int setExposureTime(std::string orientation, int value);
     /*设置亮度*/
     int setBrightness(std::string orientation, int value);
     /*设置伽马值*/
     int setGamma(std::string orientation, int value);
     /*设置对比度*/
     int setContrast(std::string orientation, int value);
     /*设置饱和度*/
     int setSatuation(std::string orientation, int value);
     /**设置白平衡
      * cv::Point(蓝， 红); (-1, -1)为不设置, (0, 0)为自动曝光
      */
     int setWhiteBalance(std::string orientation, cv::Point2i value);
     /*设置白平衡*/
     int setWhiteBalance(std::string orientation, int value);
     /*设置增益*/
     int setGain(std::string orientation, int value);
     /*设置锐度*/
     int setSharpness(std::string orientation, int value);
     /*检查输入路径是否为视频*/
     bool checkVideo(std::string path);
 
     // friend bool UsbCapture::recordVideo(std::string fileName = "");
    public:
     /**
      * status
      */
     std::atomic<bool> is_running;
     /**
      * @deprecated deprecated
      */
     UVCCamera(std::string, uint32_t, uint32_t);
 
     /**
      * @brief 构造函数，开启线程
      *
      * @param name_ID yaml里的第一级结构后方编号
      */
     UVCCamera(int name_ID);
     /**
      * @brief 构造函数，全为默认值
      *
      */
     UVCCamera();
     /**
      * @brief 析构函数，同时关闭线程
      *
      */
     ~UVCCamera();
 
     /**
      * @brief 输出图像，同opencv
      *
      * @param img 用来返回图像
      * @return int
      */
     int operator>>(cv::Mat& img);
     /**
      * @brief 同>>运算符，输出图像
      *
      * @return int
      */
     int getImage(cv::Mat&);
     /**
      * @brief 根据相机名称读取
      *
      * @return int
      */
     int getImage(std::string orientation, cv::Mat& img);
     /**
      * @brief 输出摄像头参数 ps: $ lsusb -d 114d:8589 -v
      *
      * @return int
      */
     int infoPrint();
     /**
      * @brief 设置摄像机状态，具体参见captureState参数
      *
      * @param captureState 0 读取;1 暂停，中止; 2 退出循环,终止线程
      * @return int
      */
     int setCaptureState(uint8_t captureState);
 };
 
 }  // namespace wmj
 
 #endif  // USBCAPTURE_WITH_THREAD_H
 